Prevent URI traversal for GitHub refs - #2122
Conversation
|
Thanks for the quick turnaround. As the reporter, this closes the vector I reported: quoting the ref with safe="" percent-encodes the slashes, so it lands as one opaque path segment and libcurl has no /../ to collapse before the request goes out. Good catch also adding the "." / ".." guards on user and repo, which handle the /build/gh/../x/HEAD variant that quoting the ref alone wouldn't. I checked the other interpolation sites too (get_repo_url, get_resolved_ref_url): they build github.com URLs rather than the token-bearing api path, and user/repo can't contain slashes and are now guarded, so I don't see a residual. That's from reading the diff rather than running the full suite. Looks good to me. |
|
I re-run |
|
Hmm, still failing, but I also see it is failing on |
|
Dear @Yann-P, Thanks for the pull request. I did not fully understand the problem here and I appreciate a more concrete example of the issue. Feel free to comment in https://github.com/jupyterhub/binderhub/security/advisories/GHSA-q276-fxp7-xhx3. Normally, the user will make a request like where
You reported that a user with malicious intentions could make a request like but to my knowledge, the request would not match a existing endpoint. |
This defect exposes an oracle for existence of private repos or refs through the Binder operator's github token. Fixes https://github.com/jupyterhub/binderhub/security/advisories/GHSA-q276-fxp7-xhx3.
| self.user, self.repo, self.unresolved_ref = tokenize_spec(self.spec) | ||
| self.repo = strip_suffix(self.repo, ".git") | ||
|
|
||
| # Prevents traversal (e.g. "../repo/HEAD"), see GHSA-q276-fxp7-xhx3 |
There was a problem hiding this comment.
In addition to forbidding . and .. we could tighten up the allowed characters:
- https://github.com/shinnn/github-username-regex for usernames
- Github username may only contain alphanumeric characters or hyphens.
- Github username cannot have multiple consecutive hyphens.
- Github username cannot begin or end with a hyphen.
- Maximum is 39 characters.
- https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository for repos
Type a name for your repository and an optional description. The repository name must not exceed 100 characters, and can only contain ASCII letters, digits, and the characters ., -, and _.
|
Hello @rgaiacs, I answered here: https://github.com/jupyterhub/binderhub/security/advisories/GHSA-q276-fxp7-xhx3#advisory-comment-258441 Consequently:
Let me know if this resolution works for you |
This defect exposes an oracle for existence of private repos or refs through the Binder operator's github token.
Fixes
https://github.com/jupyterhub/binderhub/security/advisories/GHSA-q276-fxp7-xhx3.
I am sending this fix through a regular PR and not a private GHSA fork, because the vulnerability is not severe, and private forks have a lot of friction (no CI, 500 errors, easy to miss when releasing).
Checklist
What does this PR do?
Type of change:
Is this PR related to an issue, or is it part of a larger body of work?
https://github.com/jupyterhub/binderhub/security/advisories/GHSA-q276-fxp7-xhx3
Does this PR introduce a breaking change?
No
How can this PR be tested?
Claude-generated end to end test procedure
File 1 — listener.py (fake api.github.com)
File 2 — bhub_config.py (standalone BinderHub, GitHub provider aimed at the listener)
(Auth is off by default → the route is anonymous, exactly the default deployment mode.)
Run
Fire the two attacks
--path-as-isis required so curl doesn't collapse the .. itself — we want BinderHub's tornado to receive the raw path. The Accept: text/event-stream header is required or the build handler 400s.Expected results
Fixed (current HEAD):
What should a reviewer concentrate their feedback on?
Other information
AI disclosure: tests generated by Claude Code (pytest and end to end)